package com.itheima.reggie.controller;

/**
 * @author 程达
 * @PackageName:com.itheima.reggie.controller
 * @ClassName:DishController
 * @Description:
 * @date 2022/5/26 14:38
 **/

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.reggie.common.R;
import com.itheima.reggie.dto.DishDto;
import com.itheima.reggie.entity.Category;
import com.itheima.reggie.entity.Dish;
import com.itheima.reggie.entity.DishFlavor;
import com.itheima.reggie.service.CategoryService;
import com.itheima.reggie.service.DishFlavorService;
import com.itheima.reggie.service.DishService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @date ：菜品及菜品口味的相关操作，我们统一使用这一个controller即可。
 * @description ：菜品管理
 * 完成菜品相关的控制
 */
@RestController
@Slf4j
@RequestMapping("/dish")
@Api(tags = "菜品和菜品口味相关接口")
public class DishController {
    //  如果 拿菜品类型 直接接收  少了 菜品口味信息 添加菜品的时候
    @Autowired
    private DishService dishService;

    //分页查询的时候需要的数据
    @Autowired
    private CategoryService categoryService;

    @Autowired
    private DishFlavorService dishFlavorService;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * @param dishDto 专门接收前端数据的
     * @return 2.这里就要用新的前端数据模型来接受
     */
    @PostMapping
    @ApiOperation(value = "新增菜品接口")
    public R<String> save(@RequestBody DishDto dishDto) {
        log.info("dishDto:{}", dishDto.toString());

        // dishDto的数据需要往几个表里面加
        // 2.往两个表 加数据是不是属于业务操作
        dishService.saveDishAndFlavor(dishDto);
        // 业务是不是需要保存一个菜品 和多个口味

        //5.清理所有菜品的缓存数据
        //清理某个分类下面的菜品缓存数据
        //第一种方式:  清理所有菜品的缓存数据
        // Set keys = redisTemplate.keys("dish_*"); //获取所有以dish_xxx开头的key
        //  redisTemplate.delete(keys); //删除这些key

        //第二种方式：清理某个分类下面的菜品缓存数据
        // 保存成功 说明redis不是最新数据了  根据categoryid 删除对应的键
        String key = "dish_"+dishDto.getCategoryId();
        redisTemplate.delete(key);//删除指定key

        return R.success("新增菜品成功");
    }

    /**
     * 菜品分页信息查询   下面这种拷贝数据技术是因为   sql语句的拼接是以 dish为准的
     */
    @GetMapping("/page")
    @ApiOperation(value = "菜品分页查询接口")
    public R<Page> page(int page, int pageSize, String name) {
        log.info("[菜品分页操作],{},{},{}", page, pageSize, name);
      /*
        分页查询套路
           构建 分页构造器
           构建 条件构造器
           调用page方法
       */
        //构建page对象
        Page<Dish> pageInfo = new Page(page, pageSize);
        //根据条件 构建条件构造器       1). 构造分页条件对象
        LambdaQueryWrapper<Dish> wrapper = new LambdaQueryWrapper<>();

        //首先 是不是 name的模糊查询    2). 构建查询及排序条件//isNotEmpty: 是不是空
        wrapper.like(StringUtils.isNotEmpty(name), Dish::getName, name);
        // 如果还想要按照 更新日期倒序   3). 执行分页条件查询
        wrapper.orderByDesc(Dish::getUpdateTime);//日期倒序
        //执行page方法  4). 遍历分页查询列表数据
        dishService.page(pageInfo, wrapper);

        // 查询到的 菜品数据里面是没有  菜品分类名称的
        // 前端看不到 菜品分类名称 原因就是 Dish类中 不包含 菜品分类名称
        // Dish类要跟 表 对应
        // DishDto里面加   它不需要跟表对应 ,前端缺啥数据 我补啥
        // 应该 把  Page<Dish> pageInfo === Page<DishDto> dishDtopage
        //4). 把Dish对象转为DishDto对象，同时赋值分类名称
        Page<DishDto> dishDtoPage = new Page<>();
        // 这个对象啥也没有   需要把 Page<Dish> pageInfo  原有的属性数据
        // 给  Page<DishDto> dishDtopage
        // 老师交给大家一个好玩的  可以完成 对象属性的复制
//        BeanUtils.copyProperties(pageInfo,dishDtoPage);
//        // 直接 这样做 是有什么属性 复制什么属性
//                               // 把 pageInfo 对应的属性值
//                             //    复制 给 dishDtoPage 的属性
        //是page上所有属性都要复制过去吗?
        // 是不是在页面中显示的数据 是要 进行 变化的!!!
        // pageInfo  records属性 List<Dish>
        // dishDtoPage  records属性 List<DishDto>
        // 分页信息复制的时候要排除
        //4.2 spring提供的一个普通ben复制属性的 工具类对象 属性的复制 的静态方法
        BeanUtils.copyProperties(pageInfo, dishDtoPage, "records");
        // 4.3 dishDtoPage 除了 records 其他属性都搞定，没复制这个records集合，先搞个集合，然后放里面放数据
        List<DishDto> records = new ArrayList<>();
        // 当前页显示的数据   records 里面啥都没有   这里面数据需要从哪里来?
        //  records  DishDto  ==Dish里面有的属性 DishDto都有
        //4.4拿到运来的集合数据，只是少了个分类name而已pageInfo这里面有想要的数据，
        List<Dish> dishRecords = pageInfo.getRecords();
        //        菜品的基本分页数据
        // 是不是应该 遍历 菜品分页基本数据 把基本数据转换成 DishDto 存到  records里面
        //  遍历拿到每个 Dish基本菜品数据    把Dish--DishDto 菜品扩展数据
        //     DishDto 还可以在添加 分类名称字段  DishDto封装好了 存到  records 集合
        //4.5）  遍历分页查询列表数据
        for (Dish dish : dishRecords) {
            // 拿到了当前页查询到的每一个 基本菜品数据 dish  ==>没有菜品分类名称
            // DishDto 扩展菜品数据 包含了 基本菜品数据的所有属性
            DishDto dishDto = new DishDto();//dishDto是不是还没都属性，创建dishDto给他复制 值
            // 应该把 dish所有属性  给 dishDto
            BeanUtils.copyProperties(dish, dishDto);// dishDto 你有的我都有
            // 你没有的我也能有
            //  5.这里的 分类名称 是不是应给根据 categoryId进行查询把少的那个分类name给补上，
            //  5.2先去查询到，在拿到里面的name属性给dto设置上值
            Category category = categoryService.getById(dish.getCategoryId());
            dishDto.setCategoryName(category.getName());
            // 6.还得把 这个升级了的菜品数据 放到当前页list中 因为就是在给这个集合完善数据
            records.add(dishDto);
        }
        //7   5). 封装数据并返回
        dishDtoPage.setRecords(records);
        //8.把封装好的pagg对象属性响应给前端
        return R.success(dishDtoPage);

    }

    /**
     * 根据菜品id查询菜品信息
     * 请求路径 /dish/15755675687898
     * 按照以前的 直接返回 dish数据 是不够的
     * 单独进行单表的查询还合适吗?
     * dish查菜品口味信息  就是因为没有菜品口味信息才导致
     * 无法正常回显
     * dish不能加,没关系  dish的子类 dishDto加了
     */
    @GetMapping("/{id}")
    @ApiOperation(value = "根据菜品id查询菜品信息接口")
    public R<DishDto> getById(@PathVariable Long id) {
        log.info("[查询的id是:]" + id);
        // 因为没有对应dishDto表  我们需要定制这个查询方法
        ///  就不能用mp提供的方法直接使用了
        //  作用 在组装 dishDto
        DishDto dishDto = dishService.getByIdWithFlavor(id);

        return R.success(dishDto);
    }

    /**
     * 根据id 更新菜品信息 以及 菜品口味信息
     * 请求参数写谁合适?
     * 怎么确定
     * 一看  包含dish基本信息+口味信息
     * 二选   DishDto
     */
    @PutMapping
    @ApiOperation(value = "根据id 更新菜品信息 以及 菜品口味信息接口")
    public R<String> update(@RequestBody DishDto dishDto) {
        log.info("[根据id 更新菜品],dishDto:{}", dishDto);
        // dishService有没有方法 可以把 菜品信息和口味信息同时更新的?
        // 木有 因为mp提供的是单表  需要我们"寄几个"来完善
        dishService.updateWithFlavor(dishDto);
        //5.清理所有菜品的缓存数据
        //清理某个分类下面的菜品缓存数据
        //第一种方式:  清理所有菜品的缓存数据
        // Set keys = redisTemplate.keys("dish_*"); //获取所有以dish_xxx开头的key
        //  redisTemplate.delete(keys); //删除这些key

        //第二种方式：清理某个分类下面的菜品缓存数据
        //        注意: 在这里我们推荐使用第一种方式进行清理，
        //        这样逻辑更加严谨。 因为对于修改操作，用户是可以修改菜品的分类的，如果用户修改了菜品的分类，
        //        那么原来分类下将少一个菜品，新的分类下将多一个菜品，这样的话，两个分类下的菜品列表数据都发生了变化。
        // 更新成功 说明redis不是最新数据了  根据categoryid 删除对应的键
        String key = "dish_"+dishDto.getCategoryId();
        redisTemplate.delete(key);//删除指定ke

        return R.success("更新菜品及菜品口味成功");
    }

//    /**
//     * 根据条件查询对应的菜品数据
//     * 根据 分类id
//     * 分类id 数据 dish一个条件
//     * 根据啥查 都能帮你 只要你是 dish一个条件
//     * 格局打开了.....来者不拒
//     */
//    @GetMapping("/list")
//    public R<List<Dish>> list(Dish dish) {
//        log.info("dish信息：{}", dish);
//        //根据传过来分类id去查询
//        //2.条件构造器 构建查询条件:注意这里要指定泛型，否则wrapper get不出来表中的字段
//        LambdaQueryWrapper<Dish> wrapper = new LambdaQueryWrapper<>();
//        //3.加条件: 根据传过来分类id去查询(要判断)
//        wrapper.eq(dish.getCategoryId() != null, Dish::getCategoryId, dish.getCategoryId());
//        //4.套餐里面加停售的还是启售的   启售的  status 是1 的
//        wrapper.eq(Dish::getStatus, 1);//直接给要比的参数值
//        //5.你想排就排  --- 有排序字段  没有就按照 更新日期倒序
//        wrapper.orderByDesc(Dish::getUpdateTime);
//        //6.满足条件后调用service层的方法
//        List<Dish> list = dishService.list(wrapper);
//        //7.把数据响应给页面 // 要成功后执行的
//
//        return R.success(list);
//
//    }

    /**
     * 根据条件查询对应的菜品数据
     * 根据 分类id
     * 分类id 数据 dish一个条件
     * 根据啥查 都能帮你 只要你是 dish一个条件
     * 格局打开了.....来者不拒
     */
    @GetMapping("/list")
    @ApiOperation(value = "根据条件查询对应的菜品接口")
    public R<List<DishDto>> list(Dish dish) {
        // 8.从缓存读取列表数据
        //定义一个用于接收 该分类下菜品 集合的对象
        List<DishDto> redisDtoList = null;
        // 先去 redis 中 找一下 有没有 该分类下的 菜品清单
        //  redis存储 规律是  dish_categoryId
        // dish_categoryId_1
        //从 redis中去读取
        String key = "dish_"+dish.getCategoryId();
        redisDtoList = (List<DishDto>) redisTemplate.opsForValue().get(key);
        log.info("从redis中读取的集合数据:"+redisDtoList);
        if(redisDtoList!=null){// 缓存中有该分类数据
            log.info("我们的数据是从redis中获取的 没有查询数据库...");
            return R.success(redisDtoList);
        }


        //构建查询条件
        LambdaQueryWrapper<Dish> wrapper = new LambdaQueryWrapper<>();
        //加条件
        wrapper.eq(dish.getCategoryId() != null, Dish::getCategoryId, dish.getCategoryId());
        // 套餐里面加停售的还是启售的   启售的  status 是1 的
        wrapper.eq(Dish::getStatus, 1);
        //你想排就排  --- 有排序字段  没有就按照 更新日期倒序
        wrapper.orderByDesc(Dish::getUpdateTime);
        List<Dish> list = dishService.list(wrapper);

        // 返回 List<DishDto>
        //  把 每一个dish---DishDto  dishDto 根据 菜品id查询到 对应的 口味信息
        //封装到 sishDto的 flavors  属性中
        List<DishDto> dishDtoList = new ArrayList<>();
        for (Dish dish1 : list) {
            DishDto dishDto = new DishDto();
            BeanUtils.copyProperties(dish1, dishDto);
            // 根据拿到的 dishid 去查询相关的  口味信息
            Long dishId = dish1.getId();
            LambdaQueryWrapper<DishFlavor> wrapper1 = new LambdaQueryWrapper<>();
            wrapper1.eq(DishFlavor::getDishId, dishId);
            List<DishFlavor> flavors = dishFlavorService.list(wrapper1);
            dishDto.setFlavors(flavors);
            Category category = categoryService.getById(dish.getCategoryId());
            dishDto.setCategoryName(category.getName());

            dishDtoList.add(dishDto);
        }
        //如果redis没数据，如果不存在，需要查询数据库，将查询到的菜品数据缓存到Redis
        redisTemplate.opsForValue().set(key, dishDtoList, 60, TimeUnit.MINUTES);// 过期时间可以设置 可以不用设置
        return R.success(dishDtoList);
    }

}
